/*
 * GISGraphView.java
 */
package gisgraph;

import edu.uci.ics.jung.algorithms.layout.*;
import edu.uci.ics.jung.graph.*;
import edu.uci.ics.jung.graph.util.EdgeType;
import edu.uci.ics.jung.graph.util.Pair;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.EditingModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import gisgraph.components.*;
import gisgraph.components.visualization.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Stroke;
import org.jdesktop.application.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import javax.swing.Timer;
import javax.swing.Icon;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JPopupMenu;
import javax.swing.filechooser.FileFilter;
import org.apache.commons.collections15.Transformer;
import org.jgrapht.experimental.dag.DirectedAcyclicGraph;
import org.jgrapht.experimental.dag.DirectedAcyclicGraph.CycleFoundException;

/**
 * The application's main frame.
 */
public class GISGraphView extends FrameView {

    private DirectedSparseGraph<StateVertex, GISEdge> stateGraph = null;
    private List<GISVertex> firstPath = null;
    private List<GISVertex> secondPath = null;    
    private List<GISVertex> thirdPath = null;    
    
    public GISGraphView(SingleFrameApplication app) {
        super(app);

        initComponents();

        // status bar initialization - message timeout, idle icon and busy animation, etc
        ResourceMap resourceMap = getResourceMap();
        int messageTimeout = resourceMap.getInteger("StatusBar.messageTimeout");
        messageTimer = new Timer(messageTimeout, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                statusMessageLabel.setText("");
            }
        });
        messageTimer.setRepeats(false);
        int busyAnimationRate = resourceMap.getInteger("StatusBar.busyAnimationRate");
        for (int i = 0; i < busyIcons.length; i++) {
            busyIcons[i] = resourceMap.getIcon("StatusBar.busyIcons[" + i + "]");
        }
        busyIconTimer = new Timer(busyAnimationRate, new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                busyIconIndex = (busyIconIndex + 1) % busyIcons.length;
                statusAnimationLabel.setIcon(busyIcons[busyIconIndex]);
            }
        });
        idleIcon = resourceMap.getIcon("StatusBar.idleIcon");
        statusAnimationLabel.setIcon(idleIcon);
        progressBar.setVisible(false);

        // connecting action tasks to status bar via TaskMonitor
        TaskMonitor taskMonitor = new TaskMonitor(getApplication().getContext());
        taskMonitor.addPropertyChangeListener(new java.beans.PropertyChangeListener() {

            public void propertyChange(java.beans.PropertyChangeEvent evt) {
                String propertyName = evt.getPropertyName();
                if ("started".equals(propertyName)) {
                    if (!busyIconTimer.isRunning()) {
                        statusAnimationLabel.setIcon(busyIcons[0]);
                        busyIconIndex = 0;
                        busyIconTimer.start();
                    }
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(true);
                } else if ("done".equals(propertyName)) {
                    busyIconTimer.stop();
                    statusAnimationLabel.setIcon(idleIcon);
                    progressBar.setVisible(false);
                    progressBar.setValue(0);
                } else if ("message".equals(propertyName)) {
                    String text = (String) (evt.getNewValue());
                    statusMessageLabel.setText((text == null) ? "" : text);
                    messageTimer.restart();
                } else if ("progress".equals(propertyName)) {
                    int value = (Integer) (evt.getNewValue());
                    progressBar.setVisible(true);
                    progressBar.setIndeterminate(false);
                    progressBar.setValue(value);
                }
            }
        });

        createGraph();
        initFileChooser();
        txConsole.setFont(new Font("Arial", Font.PLAIN, 14));
    }

    private void initFileChooser() {
        fc = new JFileChooser();
        fc.setCurrentDirectory(new File("."));
        FileFilter type = new ExtensionFilter("Pliki xml", ".xml");
        fc.addChoosableFileFilter(type);
        fc.setAcceptAllFileFilterUsed(false);
    }

    private void createGraph() {
        DirectedSparseGraph<GISVertex, GISEdge> g = new DirectedSparseGraph<GISVertex, GISEdge>();

        GISVertex v1 = GISGraphApp.vFactory.create();
        GISVertex v2 = GISGraphApp.vFactory.create();
        GISVertex v3 = GISGraphApp.vFactory.create();
        GISVertex v4 = GISGraphApp.vFactory.create();
        v4.setIsEndpoint(true);

        GISVertex v5 = GISGraphApp.vFactory.create();
        v5.setIsStartPoint(true);

        GISVertex v6 = GISGraphApp.vFactory.create();

        g.addVertex(v1);
        g.addVertex(v2);
        g.addVertex(v3);
        g.addVertex(v4);
        g.addVertex(v5);
        g.addVertex(v6);

        g.addEdge(GISGraphApp.eFactory.create(), v1, v2, EdgeType.DIRECTED);
        g.addEdge(GISGraphApp.eFactory.create(), v1, v3, EdgeType.DIRECTED);
        g.addEdge(GISGraphApp.eFactory.create(), v3, v2, EdgeType.DIRECTED);
        g.addEdge(GISGraphApp.eFactory.create(), v2, v4, EdgeType.DIRECTED);
        g.addEdge(GISGraphApp.eFactory.create(), v1, v5, EdgeType.DIRECTED);
        g.addEdge(GISGraphApp.eFactory.create(), v6, v2, EdgeType.DIRECTED);

        LoadGraphLayout(g);
        setStatusText("Utworzono przykładowy graf");
    }

    public void LoadGraphLayout(DirectedSparseGraph<GISVertex, GISEdge> graph) {
        
        setStatusText("Wczytywanie grafu...");
        
        Layout<GISVertex, GISEdge> layout = new FRLayout2(graph);
        layout.setSize(new Dimension(400, 400)); // sets the initial size of the space
        
        // The BasicVisualizationServer<V,E> is parameterized by the edge types
        
        if (vv == null) {
            vv = new VisualizationViewer<GISVertex, GISEdge>(layout);
            
            vv.setPreferredSize(new Dimension(450, 450));
            
            final Stroke edgeStroke = new BasicStroke(2);
            final Stroke edgeThickStroke = new BasicStroke(4);
            final Font edgeFont = new Font("Times New Roman", Font.BOLD, 16);

            Transformer<GISEdge, Stroke> edgeStrokeTransformer =
                    new Transformer<GISEdge, Stroke>() {

                        public Stroke transform(GISEdge s) {

                            if (s.getIsReliable()) {
                                return edgeThickStroke;
                            } else {
                                return edgeStroke;
                            }
                        }
                    };

            Transformer<GISVertex, Paint> vertexPaint = new Transformer<GISVertex, Paint>() {

                public Paint transform(GISVertex i) {
                    if (i.getIsEndPoint()) {
                        return Color.RED;
                    } else if (i.getIsStartPoint()) {
                        return Color.YELLOW;
                    } else {
                        return Color.GREEN;
                    }
                }
            };
            
            Transformer<GISEdge, Font> edgeFontTransformer = new Transformer<GISEdge, Font>() {
                public Font transform(GISEdge i) {                    
                    return edgeFont;
                }
            };

            GISEdgeDrawPaintTransformer edgePaintTransformer = new GISEdgeDrawPaintTransformer(this);

            // ustawianie pogrubiania krawędzi
            vv.getRenderContext().setEdgeStrokeTransformer(edgeStrokeTransformer);
            vv.getRenderContext().setEdgeDrawPaintTransformer(edgePaintTransformer);
            
            // Malowanie wierzchołków
            vv.getRenderContext().setVertexFillPaintTransformer(vertexPaint);

            // etykieta wierzchołka
            vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
            vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller());
            vv.getRenderContext().setEdgeFontTransformer(edgeFontTransformer);
            
            vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);

            EditingModalGraphMouse gm =
                    new EditingModalGraphMouse(
                                    vv.getRenderContext(), 
                                    GISGraphApp.vFactory, 
                                    GISGraphApp.eFactory);
            
            vv.setGraphMouse(gm);

            PopupVertexEdgeMenuMousePlugin myPlugin = new PopupVertexEdgeMenuMousePlugin();
            
            
            
            // Add some popup menus for the edges and vertices to our mouse plugin.
            JPopupMenu edgeMenu = new MyMouseMenus.EdgeMenu(this.getFrame());
            JPopupMenu vertexMenu = new MyMouseMenus.VertexMenu();
            myPlugin.setEdgePopup(edgeMenu);
            myPlugin.setVertexPopup(vertexMenu);
            gm.remove(gm.getPopupEditingPlugin());  // Removes the existing popup editing plugin

            gm.add(myPlugin);   // Add our new plugin to the mouse

            JMenu modeMenu = gm.getModeMenu(); // Obtain mode menu from the mouse
            
            modeMenu.getItem(0).setText("Transformowanie");
            modeMenu.getItem(1).setText("Podnoszenie");
            modeMenu.getItem(2).setText("Edycja");
            
            modeMenu.setText("Tryb myszki");
            modeMenu.setIcon(null); // I'm using this in a main menu
            modeMenu.setPreferredSize(new Dimension(80,20)); // Change the size
            
            menuBar.add(modeMenu, 3);

            spGraph.setViewportView(vv);
        } else {
            vv.setGraphLayout(layout);
        }
        
        resetAlgorithm();
    }

    private DirectedAcyclicGraph<GISVertex, JGraphEdge> getGraph() throws CycleFoundException
    {
        DirectedSparseGraph<GISVertex, GISEdge> g = (DirectedSparseGraph)vv.getGraphLayout().getGraph();
        
        return GraphConverter.ConvertToJGraph(g);
    }
    
    private boolean validateGraph(DirectedAcyclicGraph<GISVertex, JGraphEdge> g)
    {
        Set<GISVertex> vertices = g.vertexSet();
        
        int startPointCount = 0;
        int endPointCount = 0;
        
        for (GISVertex v : vertices)
        {
            if (v.getIsEndPoint())
                endPointCount++;
            
            if (v.getIsStartPoint())
                startPointCount++;
        }
        
        if (startPointCount != 1 || endPointCount != 1)
        {
            return false;
        }
        
        return true;
    }
    
    public void setStatusText(String text)
    {
        ConsoleWriteLine(text);
        lblStatus.repaint();
    }
    
    public void resetAlgorithm()
    {
        firstPath = null;
        secondPath = null;
        thirdPath = null;
        
        miFindPaths.setEnabled(false);
        btnShowStateGraph.setEnabled(false);
    }
    
    public Paint getEdgePaint(GISEdge i) {
        
        if (firstPath == null || secondPath == null || thirdPath == null)
            return Color.BLACK;

        Pair<GISVertex> eps = vv.getGraphLayout().getGraph().getEndpoints(i);
        
        GISVertex v1 = eps.getFirst();
        GISVertex v2 = eps.getSecond();
        
        boolean isFirstPath = false;
        boolean isSecondPath = false;
        boolean isThirdPath = false;

        int fpV1Ind = firstPath.indexOf(v1);
        int fpV2Ind = firstPath.indexOf(v2);
        
        int spV1Ind = secondPath.indexOf(v1);
        int spV2Ind = secondPath.indexOf(v2);
        
        int tpV1Ind = thirdPath.indexOf(v1);
        int tpV2Ind = thirdPath.indexOf(v2);
        
        if (fpV1Ind != -1 && fpV2Ind != -1 && ((fpV1Ind + 1) == fpV2Ind))
            isFirstPath = true;
        
        if (spV1Ind != -1 && spV2Ind != -1 && ((spV1Ind + 1) == spV2Ind))
            isSecondPath = true;
        
        if (tpV1Ind != -1 && tpV2Ind != -1 && ((tpV1Ind + 1) == tpV2Ind))
            isThirdPath = true;

        if (isFirstPath || isSecondPath || isThirdPath)
        {
            // dwie ścieżki przez jedną - zieleń
            if ((isFirstPath && isSecondPath)
                || (isFirstPath && isThirdPath)
                || (isSecondPath && isThirdPath))
            {
                return Color.GREEN;
            }
            
            if (isFirstPath)
                return Color.BLUE;
            
            if (isSecondPath)
                return Color.YELLOW;
                        
            if (isThirdPath)
                return Color.ORANGE;
        }
        
        return Color.BLACK;
    }
    
    public void ConsoleWriteLine(String lineOfText)
    {
        String text = txConsole.getText();
        
        text = text + "\n> " + lineOfText; 
        
        txConsole.setText(text);
        txConsole.repaint();
    }
    
    
    @Action
    public void saveGraph() {

        //JFrame mainFrame = GISGraphApp.getApplication().getMainFrame();
        
        fc.setApproveButtonText("Zapisz");

        int returnVal = fc.showOpenDialog(this.getFrame());

        if (returnVal == JFileChooser.APPROVE_OPTION) {

            DirectedSparseGraph<GISVertex, GISEdge> g =
                    (DirectedSparseGraph<GISVertex, GISEdge>) vv.getGraphLayout().getGraph();

            File file = fc.getSelectedFile();

            String filePath = file.getAbsolutePath();

            if (filePath.endsWith(".xml") == false) {
                filePath = filePath + ".xml";
            }

            try {
                GraphHelper.write(g, filePath);
                setStatusText("Zapisano graf");
            } catch (Exception ex) {
                System.out.println("exc");
            }
        }
    }

    @Action
    public void loadGraph() throws CycleFoundException {
        JFrame mainFrame = getFrame();
        
        fc.setApproveButtonText("Otwórz");
        int returnVal = fc.showOpenDialog(mainFrame);

        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();

            DirectedSparseGraph<GISVertex, GISEdge> g = GraphHelper.read(file.getAbsolutePath(), this);
            LoadGraphLayout(g);
            resetAlgorithm();
            setStatusText("Wczytano graf");
        }
    }

    @Action
    public void showAboutBox() {
        if (aboutBox == null) {
            JFrame mainFrame = getFrame();
            aboutBox = new GISGraphAboutBox(mainFrame);
            aboutBox.setLocationRelativeTo(mainFrame);
        }
        GISGraphApp.getApplication().show(aboutBox);
    }

    @Action
    public void showGenerateBox() {

        JFrame mainFrame = getFrame();
        generateBox = new GISGraphGenerateBox(mainFrame, this);
        generateBox.setLocationRelativeTo(mainFrame);

        GISGraphApp.getApplication().show(generateBox);
    }

    @Action
    public void makeStateGraph() {
        
        setStatusText("Tworzenie grafu stanów...");

        GISAlgorithm algorithm = new GISAlgorithm();
        
        DirectedAcyclicGraph<GISVertex, JGraphEdge> g;
        
        try {
            g = getGraph();
        } catch (CycleFoundException ex) {
            setStatusText("Utworzony graf nie jest acykliczny!");
            return;
        }
        
        boolean isValidated = validateGraph(g);
        
        if (!isValidated)
        {
            ConsoleWriteLine("Zła liczba pkt. końcowych i/lub początkowych!");
            return;
        }

        this.stateGraph = GraphConverter.ConvertStateGraphToJung(algorithm.start(g, this));
        
        btnShowStateGraph.setEnabled(true);
        miFindPaths.setEnabled(true);

        ConsoleWriteLine("Wygenerowano graf stanów");
    }
    
    @Action
    public void showStateGraph()
    {
        if (stateGraph == null)
        {
            setStatusText("Nie utworzono jeszcze grafu stanów...");
        }
        
        JFrame mainFrame = getFrame();
        stateGraphBox = new StateGraphBox(mainFrame, false, stateGraph);
        stateGraphBox.setLocationRelativeTo(mainFrame);

        GISGraphApp.getApplication().show(stateGraphBox);
        
        setStatusText("Pokazano graf stanów");
    }

    @Action
    public void findPaths()
    {
        
        ConsoleWriteLine("Generowanie ścieżek...");
        
        try {
            DirectedAcyclicGraph<GISVertex, JGraphEdge> g = getGraph(); 
            
            GISVertex source = null;
            GISVertex target = null;
            
            for(GISVertex v : g.vertexSet())
            {
                if (v.getIsEndPoint())
                    target = v;
                else if (v.getIsStartPoint())
                    source = v;
            }

            firstPath = new ArrayList<GISVertex>();
            secondPath = new ArrayList<GISVertex>();
            thirdPath = new ArrayList<GISVertex>();
            
            HashMap<Integer, GISVertex> vertexMap = new HashMap<Integer, GISVertex>();
            
            for (GISVertex v : vv.getGraphLayout().getGraph().getVertices())
            {
                vertexMap.put(v.getId(), v);
            }

            List<GISEdge> path = GISAlgorithm.findPaths(stateGraph, source, target, this);
            
            if (path.size() == 0)
            {
                ConsoleWriteLine("Nie udało się znaleźć 3 ścieżek!");
                return;
            }
            
            firstPath.add(source);
            secondPath.add(source);
            thirdPath.add(source);
            
            for (GISEdge e : path)
            {
                Pair<StateVertex> eps = stateGraph.getEndpoints(e);
                StateVertex v2 = eps.getSecond();
                StateVertex v1 = eps.getFirst();
                
                List<GISVertex> newVertices = new ArrayList<GISVertex>();

                newVertices.add(vertexMap.get(v2.getId1()));
                newVertices.add(vertexMap.get(v2.getId2()));
                newVertices.add(vertexMap.get(v2.getId3()));
                                
                GISVertex fpLast = firstPath.get(firstPath.size() - 1);
                GISVertex spLast = secondPath.get(secondPath.size() - 1);
                GISVertex tpLast = thirdPath.get(thirdPath.size() - 1);

                
                boolean fAdded = false;
                boolean sAdded = false;
                boolean tAdded = false;
                
                for(GISVertex newV : newVertices)
                {
                  /*  if (newV.getId() == fpLast.getId()
                            || newV.getId() == spLast.getId()
                            || newV.getId() == tpLast.getId())
                    {
                        continue;
                    }*/
                    
                    if (newV.getId() == fpLast.getId() && !fAdded)
                    {
                        fAdded = true;
                        continue;
                    }
                    
                    if (newV.getId() == spLast.getId() && !sAdded)
                    {
                        sAdded = true;
                        continue;
                    }
                    
                    if (newV.getId() == tpLast.getId() && !tAdded)
                    {
                        tAdded = true;
                        continue;
                    }
                    
                    
                    if (!fAdded && newV.getId() != fpLast.getId())
                    {
                        // czy istnieje krawędź
                        if (g.containsEdge(fpLast, newV))
                        {
                            firstPath.add(newV);
                            fAdded = true;
                            continue;
                        }
                    }
                    
                    if (!sAdded && newV.getId() != spLast.getId())
                    {
                        // czy istnieje krawędź
                        if (g.containsEdge(spLast, newV))
                        {
                            secondPath.add(newV);
                            sAdded = true;
                            continue;
                        }
                    }
                    
                    if (!tAdded && newV.getId() != tpLast.getId())
                    {
                        // czy istnieje krawędź
                        if (g.containsEdge(tpLast, newV))
                        {
                            thirdPath.add(newV);
                            tAdded = true;
                            continue;
                        }
                    }
                }                
                
            }
            
        } catch (CycleFoundException ex) {
            setStatusText("Wystąpił błąd");
            return;
        }
        
        String path1 = "Ścieżka pierwsza: ";
        String path2 = "Ścieżka druga: ";
        String path3 = "Ścieżka: trzecia: ";

        for (GISVertex gv : firstPath)
        {
            path1 = path1 + gv.toString() + ", ";
        }
        
        for (GISVertex gv : secondPath)
        {
            path2 = path2 + gv.toString() + ", ";
        }
        
        for (GISVertex gv : thirdPath)
        {
            path3 = path3 + gv.toString() + ", ";
        }
        
        path1 = path1.trim().substring(0, path1.length() - 2);
        path2 = path2.trim().substring(0, path2.length() - 2);
        path3 = path3.trim().substring(0, path3.length() - 2);
        
        ConsoleWriteLine(path1);
        ConsoleWriteLine(path2);
        ConsoleWriteLine(path3);
        
        vv.repaint();
        ConsoleWriteLine("Zaznaczono ścieżki na grafie");
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        mainPanel = new javax.swing.JPanel();
        spGraph = new javax.swing.JScrollPane();
        pnButtons = new javax.swing.JPanel();
        btnShowStateGraph = new javax.swing.JButton();
        jPanel1 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jPanel2 = new javax.swing.JPanel();
        jLabel3 = new javax.swing.JLabel();
        jPanel4 = new javax.swing.JPanel();
        jLabel5 = new javax.swing.JLabel();
        jLabel6 = new javax.swing.JLabel();
        jPanel6 = new javax.swing.JPanel();
        jLabel7 = new javax.swing.JLabel();
        jLabel8 = new javax.swing.JLabel();
        jPanel5 = new javax.swing.JPanel();
        jScrollPane1 = new javax.swing.JScrollPane();
        txConsole = new javax.swing.JTextArea();
        menuBar = new javax.swing.JMenuBar();
        javax.swing.JMenu fileMenu = new javax.swing.JMenu();
        miFileLoad = new javax.swing.JMenuItem();
        jMenuItem6 = new javax.swing.JMenuItem();
        javax.swing.JMenuItem exitMenuItem = new javax.swing.JMenuItem();
        jMenu1 = new javax.swing.JMenu();
        jMenuItem1 = new javax.swing.JMenuItem();
        algMenu = new javax.swing.JMenu();
        jMenuItem4 = new javax.swing.JMenuItem();
        miFindPaths = new javax.swing.JMenuItem();
        javax.swing.JMenu helpMenu = new javax.swing.JMenu();
        javax.swing.JMenuItem aboutMenuItem = new javax.swing.JMenuItem();
        statusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();
        lblStatus = new javax.swing.JLabel();

        mainPanel.setName("mainPanel"); // NOI18N

        spGraph.setDoubleBuffered(true);
        spGraph.setName("spGraph"); // NOI18N

        pnButtons.setName("pnButtons"); // NOI18N

        javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(gisgraph.GISGraphApp.class).getContext().getActionMap(GISGraphView.class, this);
        btnShowStateGraph.setAction(actionMap.get("showStateGraph")); // NOI18N
        org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(gisgraph.GISGraphApp.class).getContext().getResourceMap(GISGraphView.class);
        btnShowStateGraph.setText(resourceMap.getString("btnShowStateGraph.text")); // NOI18N
        btnShowStateGraph.setName("btnShowStateGraph"); // NOI18N

        jPanel1.setBackground(resourceMap.getColor("jPanel1.background")); // NOI18N
        jPanel1.setName("jPanel1"); // NOI18N

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 13, Short.MAX_VALUE)
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 14, Short.MAX_VALUE)
        );

        jLabel1.setText(resourceMap.getString("jLabel1.text")); // NOI18N
        jLabel1.setName("jLabel1"); // NOI18N

        jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N
        jLabel2.setName("jLabel2"); // NOI18N

        jPanel2.setBackground(resourceMap.getColor("jPanel2.background")); // NOI18N
        jPanel2.setName("jPanel2"); // NOI18N

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 13, Short.MAX_VALUE)
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 14, Short.MAX_VALUE)
        );

        jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N
        jLabel3.setName("jLabel3"); // NOI18N

        jPanel4.setBackground(resourceMap.getColor("jPanel4.background")); // NOI18N
        jPanel4.setName("jPanel4"); // NOI18N

        javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4);
        jPanel4.setLayout(jPanel4Layout);
        jPanel4Layout.setHorizontalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 13, Short.MAX_VALUE)
        );
        jPanel4Layout.setVerticalGroup(
            jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 14, Short.MAX_VALUE)
        );

        jLabel5.setText(resourceMap.getString("jLabel5.text")); // NOI18N
        jLabel5.setName("jLabel5"); // NOI18N

        jLabel6.setText(resourceMap.getString("jLabel6.text")); // NOI18N
        jLabel6.setName("jLabel6"); // NOI18N

        jPanel6.setBackground(resourceMap.getColor("jPanel6.background")); // NOI18N
        jPanel6.setName("jPanel6"); // NOI18N

        javax.swing.GroupLayout jPanel6Layout = new javax.swing.GroupLayout(jPanel6);
        jPanel6.setLayout(jPanel6Layout);
        jPanel6Layout.setHorizontalGroup(
            jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 13, Short.MAX_VALUE)
        );
        jPanel6Layout.setVerticalGroup(
            jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 14, Short.MAX_VALUE)
        );

        jLabel7.setText(resourceMap.getString("jLabel7.text")); // NOI18N
        jLabel7.setName("jLabel7"); // NOI18N

        jLabel8.setText(resourceMap.getString("jLabel8.text")); // NOI18N
        jLabel8.setName("jLabel8"); // NOI18N

        javax.swing.GroupLayout pnButtonsLayout = new javax.swing.GroupLayout(pnButtons);
        pnButtons.setLayout(pnButtonsLayout);
        pnButtonsLayout.setHorizontalGroup(
            pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnButtonsLayout.createSequentialGroup()
                .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(pnButtonsLayout.createSequentialGroup()
                        .addGap(17, 17, 17)
                        .addComponent(btnShowStateGraph))
                    .addGroup(pnButtonsLayout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(pnButtonsLayout.createSequentialGroup()
                                .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel1))
                            .addGroup(pnButtonsLayout.createSequentialGroup()
                                .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel2))
                            .addGroup(pnButtonsLayout.createSequentialGroup()
                                .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel3))))
                    .addGroup(pnButtonsLayout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jLabel5))
                    .addGroup(pnButtonsLayout.createSequentialGroup()
                        .addContainerGap()
                        .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                            .addComponent(jLabel7, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addGroup(javax.swing.GroupLayout.Alignment.LEADING, pnButtonsLayout.createSequentialGroup()
                                .addComponent(jPanel6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(jLabel6))))
                    .addGroup(pnButtonsLayout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(jLabel8, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)))
                .addContainerGap())
        );
        pnButtonsLayout.setVerticalGroup(
            pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(pnButtonsLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(btnShowStateGraph)
                .addGap(18, 18, 18)
                .addComponent(jLabel5)
                .addGap(4, 4, 4)
                .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jPanel4, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel3))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(pnButtonsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jPanel6, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel6))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel7)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jLabel8)
                .addContainerGap(211, Short.MAX_VALUE))
        );

        jPanel5.setName("jPanel5"); // NOI18N

        jScrollPane1.setName("jScrollPane1"); // NOI18N

        txConsole.setColumns(20);
        txConsole.setRows(5);
        txConsole.setName("txConsole"); // NOI18N
        jScrollPane1.setViewportView(txConsole);

        javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5);
        jPanel5.setLayout(jPanel5Layout);
        jPanel5Layout.setHorizontalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 708, Short.MAX_VALUE)
        );
        jPanel5Layout.setVerticalGroup(
            jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 77, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout mainPanelLayout = new javax.swing.GroupLayout(mainPanel);
        mainPanel.setLayout(mainPanelLayout);
        mainPanelLayout.setHorizontalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(mainPanelLayout.createSequentialGroup()
                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(spGraph, javax.swing.GroupLayout.DEFAULT_SIZE, 708, Short.MAX_VALUE)
                    .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(pnButtons, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );
        mainPanelLayout.setVerticalGroup(
            mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(mainPanelLayout.createSequentialGroup()
                .addGroup(mainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(pnButtons, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addGroup(mainPanelLayout.createSequentialGroup()
                        .addComponent(spGraph, javax.swing.GroupLayout.DEFAULT_SIZE, 312, Short.MAX_VALUE)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(jPanel5, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addGap(21, 21, 21))
        );

        menuBar.setName("menuBar"); // NOI18N

        fileMenu.setText(resourceMap.getString("fileMenu.text")); // NOI18N
        fileMenu.setName("fileMenu"); // NOI18N

        miFileLoad.setAction(actionMap.get("loadGraph")); // NOI18N
        miFileLoad.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_L, java.awt.event.InputEvent.CTRL_MASK));
        miFileLoad.setText(resourceMap.getString("miFileLoad.text")); // NOI18N
        miFileLoad.setName("miFileLoad"); // NOI18N
        fileMenu.add(miFileLoad);

        jMenuItem6.setAction(actionMap.get("saveGraph")); // NOI18N
        jMenuItem6.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        jMenuItem6.setText(resourceMap.getString("jMenuItem6.text")); // NOI18N
        jMenuItem6.setName("jMenuItem6"); // NOI18N
        fileMenu.add(jMenuItem6);

        exitMenuItem.setAction(actionMap.get("quit")); // NOI18N
        exitMenuItem.setText(resourceMap.getString("exitMenuItem.text")); // NOI18N
        exitMenuItem.setToolTipText(resourceMap.getString("exitMenuItem.toolTipText")); // NOI18N
        exitMenuItem.setName("exitMenuItem"); // NOI18N
        fileMenu.add(exitMenuItem);

        menuBar.add(fileMenu);

        jMenu1.setText(resourceMap.getString("jMenu1.text")); // NOI18N
        jMenu1.setName("jMenu1"); // NOI18N

        jMenuItem1.setAction(actionMap.get("showGenerateBox")); // NOI18N
        jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_G, java.awt.event.InputEvent.CTRL_MASK));
        jMenuItem1.setText(resourceMap.getString("jMenuItem1.text")); // NOI18N
        jMenuItem1.setName("jMenuItem1"); // NOI18N
        jMenu1.add(jMenuItem1);

        menuBar.add(jMenu1);

        algMenu.setAction(actionMap.get("makeStateGraph")); // NOI18N
        algMenu.setText(resourceMap.getString("algMenu.text")); // NOI18N
        algMenu.setName("algMenu"); // NOI18N

        jMenuItem4.setAction(actionMap.get("makeStateGraph")); // NOI18N
        jMenuItem4.setText(resourceMap.getString("jMenuItem4.text")); // NOI18N
        jMenuItem4.setName("jMenuItem4"); // NOI18N
        algMenu.add(jMenuItem4);

        miFindPaths.setAction(actionMap.get("findPaths")); // NOI18N
        miFindPaths.setText(resourceMap.getString("miFindPaths.text")); // NOI18N
        miFindPaths.setName("miFindPaths"); // NOI18N
        algMenu.add(miFindPaths);

        menuBar.add(algMenu);

        helpMenu.setText(resourceMap.getString("helpMenu.text")); // NOI18N
        helpMenu.setName("helpMenu"); // NOI18N

        aboutMenuItem.setAction(actionMap.get("showAboutBox")); // NOI18N
        aboutMenuItem.setText(resourceMap.getString("aboutMenuItem.text")); // NOI18N
        aboutMenuItem.setName("aboutMenuItem"); // NOI18N
        helpMenu.add(aboutMenuItem);

        menuBar.add(helpMenu);

        statusPanel.setName("statusPanel"); // NOI18N

        statusPanelSeparator.setName("statusPanelSeparator"); // NOI18N

        statusMessageLabel.setName("statusMessageLabel"); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
        statusAnimationLabel.setName("statusAnimationLabel"); // NOI18N

        progressBar.setName("progressBar"); // NOI18N

        lblStatus.setText(resourceMap.getString("lblStatus.text")); // NOI18N
        lblStatus.setName("lblStatus"); // NOI18N

        javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(statusPanelSeparator, javax.swing.GroupLayout.DEFAULT_SIZE, 834, Short.MAX_VALUE)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(statusPanelLayout.createSequentialGroup()
                        .addContainerGap()
                        .addComponent(statusMessageLabel))
                    .addComponent(lblStatus))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 660, Short.MAX_VALUE)
                .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(statusAnimationLabel)
                .addContainerGap())
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(statusPanelLayout.createSequentialGroup()
                .addComponent(statusPanelSeparator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(statusPanelLayout.createSequentialGroup()
                        .addComponent(statusMessageLabel)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(lblStatus))
                    .addGroup(statusPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(statusAnimationLabel)
                        .addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addGap(3, 3, 3))
        );

        setComponent(mainPanel);
        setMenuBar(menuBar);
        setStatusBar(statusPanel);
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenu algMenu;
    private javax.swing.JButton btnShowStateGraph;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JLabel jLabel6;
    private javax.swing.JLabel jLabel7;
    private javax.swing.JLabel jLabel8;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JMenuItem jMenuItem4;
    private javax.swing.JMenuItem jMenuItem6;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JPanel jPanel6;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JLabel lblStatus;
    private javax.swing.JPanel mainPanel;
    private javax.swing.JMenuBar menuBar;
    private javax.swing.JMenuItem miFileLoad;
    private javax.swing.JMenuItem miFindPaths;
    private javax.swing.JPanel pnButtons;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JScrollPane spGraph;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    private javax.swing.JTextArea txConsole;
    // End of variables declaration//GEN-END:variables
    private final Timer messageTimer;
    private final Timer busyIconTimer;
    private final Icon idleIcon;
    private final Icon[] busyIcons = new Icon[15];
    private int busyIconIndex = 0;
    private JDialog aboutBox;
    private JDialog generateBox;
    private JDialog stateGraphBox;
    VisualizationViewer<GISVertex, GISEdge> vv;
    private JFileChooser fc;
}
